package com.samsunghack.apps.android.noq;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.samsunghack.apps.android.utils.ImageDownloader;
import com.samsunghack.apps.android.utils.LocationUtils;
import com.samsunghack.apps.apis.GooglePlacesData;
import com.samsunghack.apps.apis.GooglePlacesData.GooglePlaces;
import com.samsunghack.apps.apis.GooglePlacesIfc;
public class NearbyActivity extends FragmentActivity implements
LocationListener, GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
private static final String TAG="NearbyActivity";
// A request to connect to Location Services
private LocationRequest mLocationRequest;
// Stores the current instantiation of the location client in this object
private LocationClient mLocationClient;
// Handles to UI widgets
private TextView mLatLng;
private TextView mAddress;
private ProgressBar mProgressBar;
private TextView mConnectionState;
private TextView mConnectionStatus;
private ListView mRestaurantsListV;
private EditText mSearchField;
private Button mSearchButton;
private String mSearchData;
// Handle to SharedPreferences for this app
SharedPreferences mPrefs;
// Handle to a SharedPreferences editor
SharedPreferences.Editor mEditor;
/*
* Note if updates have been turned on. Starts out as "false"; is set to
* "true" in the method handleRequestSuccess of LocationUpdateReceiver.
*/
boolean mUpdatesRequested = false;
GooglePlacesData mGooglePlacesData;
ArrayList<GooglePlaces> mGooglePlacesList;
private GooglePlacesAdapter mGooglePlacesAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nearby);
// Show the Up button in the action bar.
getActionBar().setDisplayHomeAsUpEnabled(true);
mRestaurantsListV = (ListView) findViewById(R.id.restaurants_list);
mRestaurantsListV.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View view, int position,
long id) {
// Launch Reservations activity
GooglePlaces restaurantPlace = mGooglePlacesList.get(position);
Intent intent = new Intent(NearbyActivity.this,ReservationFormActivity.class);
intent.putExtra(AppConstants.RESTAURANT_NAME, restaurantPlace.getName());
intent.putExtra(AppConstants.RESTAURANT_ADDRESS, restaurantPlace.getVicinity());
startActivity(intent);
}
});
mSearchField = (EditText) findViewById(R.id.search_field);
mSearchButton = (Button) findViewById(R.id.search_button);
mSearchButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mSearchData = mSearchField.getText().toString();
String[] params = new String[3];;
params[0] = "37.37677240"; // Lat
params[1] = "-121.92164020"; // Long
params[2] = mSearchData;
new FindNearbyRestaurantsTask().execute(params);
}
});
// Get handles to the UI view objects
mLatLng = (TextView) findViewById(R.id.lat_lng);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mConnectionState = (TextView) findViewById(R.id.text_connection_state);
mConnectionStatus = (TextView) findViewById(R.id.text_connection_status);
// Create a new global location parameters object
mLocationRequest = LocationRequest.create();
/*
* Set the update interval
*/
mLocationRequest
.setInterval(LocationUtils.UPDATE_INTERVAL_IN_MILLISECONDS);
// Use high accuracy
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the interval ceiling to one minute
mLocationRequest
.setFastestInterval(LocationUtils.FAST_INTERVAL_CEILING_IN_MILLISECONDS);
// Note that location updates are off until the user turns them on
mUpdatesRequested = false;
// Open Shared Preferences
mPrefs = getSharedPreferences(LocationUtils.SHARED_PREFERENCES,
Context.MODE_PRIVATE);
// Get an editor
mEditor = mPrefs.edit();
/*
* Create a new location client, using the enclosing class to handle
* callbacks.
*/
mLocationClient = new LocationClient(this, this, this);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action buttons
switch (item.getItemId()) {
case android.R.id.home:
this.finish();
break;
default:
return super.onOptionsItemSelected(item);
}
return false;
}
/*
* Called when the Activity is no longer visible at all. Stop updates and
* disconnect.
*/
@Override
public void onStop() {
// If the client is connected
if (mLocationClient.isConnected()) {
stopPeriodicUpdates();
}
// After disconnect() is called, the client is considered "dead".
mLocationClient.disconnect();
super.onStop();
}
/*
* Called when the Activity is going into the background. Parts of the UI
* may be visible, but the Activity is inactive.
*/
@Override
public void onPause() {
// Save the current setting for updates
mEditor.putBoolean(LocationUtils.KEY_UPDATES_REQUESTED,
mUpdatesRequested);
mEditor.commit();
super.onPause();
}
/*
* Called when the Activity is restarted, even before it becomes visible.
*/
@Override
public void onStart() {
super.onStart();
/*
* Connect the client. Don't re-start any requests here; instead, wait
* for onResume()
*/
mLocationClient.connect();
}
/*
* Called when the system detects that this Activity is now visible.
*/
@Override
public void onResume() {
super.onResume();
// If the app already has a setting for getting location updates, get it
if (mPrefs.contains(LocationUtils.KEY_UPDATES_REQUESTED)) {
mUpdatesRequested = mPrefs.getBoolean(
LocationUtils.KEY_UPDATES_REQUESTED, false);
// Otherwise, turn off location updates until requested
} else {
mEditor.putBoolean(LocationUtils.KEY_UPDATES_REQUESTED, false);
mEditor.commit();
}
String[] params = new String[3];
params[0] = "37.37677240";
params[1] = "-121.92164020";
params[2] = "pizza";
// Find Nearby Restaurants
new FindNearbyRestaurantsTask().execute(params);
}
/*
* Handle results returned to this Activity by other Activities started with
* startActivityForResult(). In particular, the method onConnectionFailed()
* in LocationUpdateRemover and LocationUpdateRequester may call
* startResolutionForResult() to start an Activity that handles Google Play
* services problems. The result of this call returns here, to
* onActivityResult.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
// Choose what to do based on the request code
switch (requestCode) {
// If the request code matches the code sent in onConnectionFailed
case LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST:
switch (resultCode) {
// If Google Play services resolved the problem
case Activity.RESULT_OK:
// Log the result
Log.d(LocationUtils.APPTAG, getString(R.string.resolved));
// Display the result
mConnectionState.setText(R.string.connected);
mConnectionStatus.setText(R.string.resolved);
break;
// If any other result was returned by Google Play services
default:
// Log the result
Log.d(LocationUtils.APPTAG, getString(R.string.no_resolution));
// Display the result
mConnectionState.setText(R.string.disconnected);
mConnectionStatus.setText(R.string.no_resolution);
break;
}
// If any other request code was received
default:
// Report that this Activity received an unknown requestCode
Log.d(LocationUtils.APPTAG,
getString(R.string.unknown_activity_request_code,
requestCode));
break;
}
}
/**
* Verify that Google Play services is available before making a request.
*
* @return true if Google Play services is available, otherwise false
*/
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode = GooglePlayServicesUtil
.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
// In debug mode, log the status
Log.d(LocationUtils.APPTAG,
getString(R.string.play_services_available));
// Continue
return true;
// Google Play services was not available for some reason
} else {
// Display an error dialog
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(resultCode,
this, 0);
if (dialog != null) {
ErrorDialogFragment errorFragment = new ErrorDialogFragment();
errorFragment.setDialog(dialog);
errorFragment.show(getSupportFragmentManager(),
LocationUtils.APPTAG);
}
return false;
}
}
/**
* Invoked by the "Get Location" button.
*
* Calls getLastLocation() to get the current location
*
* @param v
* The view object associated with this method, in this case a
* Button.
*/
public void getLocation(View v) {
// If Google Play Services is available
if (servicesConnected()) {
// Get the current location
Location currentLocation = mLocationClient.getLastLocation();
// Display the current location in the UI
mLatLng.setText(LocationUtils.getLatLng(this, currentLocation));
}
}
/**
* Invoked by the "Get Address" button. Get the address of the current
* location, using reverse geocoding. This only works if a geocoding service
* is available.
*
* @param v
* The view object associated with this method, in this case a
* Button.
*/
// For Eclipse with ADT, suppress warnings about Geocoder.isPresent()
@SuppressLint("NewApi")
public void getAddress(View v) {
// In Gingerbread and later, use Geocoder.isPresent() to see if a
// geocoder is available.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD
&& !Geocoder.isPresent()) {
// No geocoder is present. Issue an error message
Toast.makeText(this, R.string.no_geocoder_available,
Toast.LENGTH_LONG).show();
return;
}
if (servicesConnected()) {
// Get the current location
Location currentLocation = mLocationClient.getLastLocation();
// Turn the indefinite activity indicator on
mProgressBar.setVisibility(View.VISIBLE);
// Start the background task
(new NearbyActivity.GetAddressTask(this)).execute(currentLocation);
}
}
/**
* Invoked by the "Start Updates" button Sends a request to start location
* updates
*
* @param v
* The view object associated with this method, in this case a
* Button.
*/
public void startUpdates(View v) {
mUpdatesRequested = true;
if (servicesConnected()) {
startPeriodicUpdates();
}
}
/**
* Invoked by the "Stop Updates" button Sends a request to remove location
* updates request them.
*
* @param v
* The view object associated with this method, in this case a
* Button.
*/
public void stopUpdates(View v) {
mUpdatesRequested = false;
if (servicesConnected()) {
stopPeriodicUpdates();
}
}
/*
* Called by Location Services when the request to connect the client
* finishes successfully. At this point, you can request the current
* location or start periodic updates
*/
@Override
public void onConnected(Bundle bundle) {
mConnectionStatus.setText(R.string.connected);
if (mUpdatesRequested) {
startPeriodicUpdates();
}
}
/*
* Called by Location Services if the connection to the location client
* drops because of an error.
*/
@Override
public void onDisconnected() {
mConnectionStatus.setText(R.string.disconnected);
}
/*
* Called by Location Services if the attempt to Location Services fails.
*/
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
/*
* Google Play services can resolve some errors it detects. If the error
* has a resolution, try sending an Intent to start a Google Play
* services activity that can resolve error.
*/
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(this,
LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);
/*
* Thrown if Google Play services canceled the original
* PendingIntent
*/
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
// If no resolution is available, display a dialog to the user with
// the error.
showErrorDialog(connectionResult.getErrorCode());
}
}
/**
* Report location updates to the UI.
*
* @param location
* The updated location.
*/
@Override
public void onLocationChanged(Location location) {
// Report to the UI that the location was updated
mConnectionStatus.setText(R.string.location_updated);
// In the UI, set the latitude and longitude to the value received
mLatLng.setText(LocationUtils.getLatLng(this, location));
}
/**
* In response to a request to start updates, send a request to Location
* Services
*/
private void startPeriodicUpdates() {
mLocationClient.requestLocationUpdates(mLocationRequest, this);
mConnectionState.setText(R.string.location_requested);
}
/**
* In response to a request to stop updates, send a request to Location
* Services
*/
private void stopPeriodicUpdates() {
mLocationClient.removeLocationUpdates(this);
mConnectionState.setText(R.string.location_updates_stopped);
}
/**
* An AsyncTask that calls getFromLocation() in the background. The class
* uses the following generic types: Location - A
* {@link android.location.Location} object containing the current location,
* passed as the input parameter to doInBackground() Void - indicates that
* progress units are not used by this subclass String - An address passed
* to onPostExecute()
*/
protected class GetAddressTask extends AsyncTask<Location, Void, String> {
// Store the context passed to the AsyncTask when the system
// instantiates it.
Context localContext;
// Constructor called by the system to instantiate the task
public GetAddressTask(Context context) {
// Required by the semantics of AsyncTask
super();
// Set a Context for the background task
localContext = context;
}
/**
* Get a geocoding service instance, pass latitude and longitude to it,
* format the returned address, and return the address to the UI thread.
*/
@Override
protected String doInBackground(Location... params) {
/*
* Get a new geocoding service instance, set for localized
* addresses. This example uses android.location.Geocoder, but other
* geocoders that conform to address standards can also be used.
*/
Geocoder geocoder = new Geocoder(localContext, Locale.getDefault());
// Get the current location from the input parameter list
Location location = params[0];
// Create a list to contain the result address
List<Address> addresses = null;
// Try to get an address for the current location. Catch IO or
// network problems.
try {
/*
* Call the synchronous getFromLocation() method with the
* latitude and longitude of the current location. Return at
* most 1 address.
*/
addresses = geocoder.getFromLocation(location.getLatitude(),
location.getLongitude(), 1);
// Catch network or other I/O problems.
} catch (IOException exception1) {
// Log an error and return an error message
Log.e(LocationUtils.APPTAG,
getString(R.string.IO_Exception_getFromLocation));
// print the stack trace
exception1.printStackTrace();
// Return an error message
return (getString(R.string.IO_Exception_getFromLocation));
// Catch incorrect latitude or longitude values
} catch (IllegalArgumentException exception2) {
// Construct a message containing the invalid arguments
String errorString = getString(
R.string.illegal_argument_exception,
location.getLatitude(), location.getLongitude());
// Log the error and print the stack trace
Log.e(LocationUtils.APPTAG, errorString);
exception2.printStackTrace();
//
return errorString;
}
// If the reverse geocode returned an address
if (addresses != null && addresses.size() > 0) {
// Get the first address
Address address = addresses.get(0);
// Format the first line of address
String addressText = getString(
R.string.address_output_string,
// If there's a street address, add it
address.getMaxAddressLineIndex() > 0 ? address
.getAddressLine(0) : "",
// Locality is usually a city
address.getLocality(),
// The country of the address
address.getCountryName());
// Return the text
return addressText;
// If there aren't any addresses, post a message
} else {
return getString(R.string.no_address_found);
}
}
/**
* A method that's called once doInBackground() completes. Set the text
* of the UI element that displays the address. This method runs on the
* UI thread.
*/
@Override
protected void onPostExecute(String address) {
// Turn off the progress bar
mProgressBar.setVisibility(View.GONE);
// Set the address in the UI
mAddress.setText(address);
}
}
/**
* Show a dialog returned by Google Play services for the connection error
* code
*
* @param errorCode
* An error code returned from onConnectionFailed
*/
private void showErrorDialog(int errorCode) {
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(errorCode,
this, LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment in which to show the error dialog
ErrorDialogFragment errorFragment = new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(getSupportFragmentManager(),
LocationUtils.APPTAG);
}
}
/**
* Define a DialogFragment to display the error dialog generated in
* showErrorDialog.
*/
public static class ErrorDialogFragment extends DialogFragment {
// Global field to contain the error dialog
private Dialog mDialog;
/**
* Default constructor. Sets the dialog field to null
*/
public ErrorDialogFragment() {
super();
mDialog = null;
}
/**
* Set the dialog to display
*
* @param dialog
* An error dialog
*/
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
/*
* This method must return a Dialog to the DialogFragment.
*/
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
}
class GooglePlacesAdapter extends ArrayAdapter<GooglePlaces> {
private final LayoutInflater mLayoutInflater;
private final ImageDownloader imageDownloader = new ImageDownloader();
GooglePlacesAdapter(NearbyActivity activity,ArrayList<GooglePlaces> GooglePlacesList) {
super(activity, 0);
mLayoutInflater = LayoutInflater.from(activity);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.list_item_nearby_restaurants, parent, false);
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.vicinity = (TextView) convertView.findViewById(R.id.vicinity);
holder.imageView = (ImageView) convertView.findViewById(R.id.image);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final GooglePlaces googlePlaces = getItem(position);
imageDownloader.download(googlePlaces.getIcon(), (ImageView) holder.imageView);
holder.name.setText(googlePlaces.getName());
holder.vicinity.setText(googlePlaces.getVicinity());
return convertView;
}
}
class ViewHolder {
ImageView imageView;
TextView name;
TextView vicinity;
}
private class FindNearbyRestaurantsTask extends AsyncTask<String, String, GooglePlacesData> {
protected GooglePlacesData doInBackground(String... params) {
// Turn the indefinite activity indicator on
// FIXME - Progress Bar is causing some issues
// if(mProgressBar!=null) {
// mProgressBar.setVisibility(View.VISIBLE);
// }
return GooglePlacesIfc.getPlaces(getApplicationContext(),params[0],params[1],params[2],"food");
}
protected void onProgressUpdate(String... progress) {
}
protected void onPostExecute(GooglePlacesData googlePlacesData) {
if(googlePlacesData!=null) {
displayGooglePlaces(googlePlacesData);
}
// if(mProgressBar!=null) {
// mProgressBar.setVisibility(View.GONE);
// }
}
protected void onPreExecute() {
}
}
private void displayGooglePlaces(GooglePlacesData googlePlacesData) {
mGooglePlacesData = googlePlacesData;
if (mGooglePlacesData != null) {
mGooglePlacesList = mGooglePlacesData.getFeatuersData();
if (mGooglePlacesList != null && mGooglePlacesList.isEmpty() == false) {
if (mGooglePlacesAdapter == null) {
mGooglePlacesAdapter = new GooglePlacesAdapter(NearbyActivity.this,mGooglePlacesList);
}
mGooglePlacesAdapter.clear();
Iterator<GooglePlaces> it = mGooglePlacesList.iterator();
while(it.hasNext()){
GooglePlaces GooglePlaces = it.next();
mGooglePlacesAdapter.add(GooglePlaces);
}
if (mGooglePlacesAdapter.isEmpty()) {
Toast.makeText(this,R.string.fatal_error, Toast.LENGTH_SHORT).show();
} else {
mRestaurantsListV.setAdapter(mGooglePlacesAdapter);
}
}
else{
Toast.makeText(getApplicationContext(), "No GooglePlaces available.", Toast.LENGTH_SHORT).show();
}
} else {
Log.e(TAG,"displayGooglePlaces: mGooglePlacesData = null");
}
}
}